<?php

/*
 * Copyright (c) 2012 Aalto University and University of Helsinki
 * MIT License
 * see LICENSE.txt for more information
 */

class VocabularyDAO {

  public $graph;

  public function __construct() {
    $this->graph = new EasyRdf_Graph();
    $this->graph->parse(file_get_contents(VOCABULARIES_FILE));
  }

  private function createDataObjects($class, $resarr) {
    $ret = array();
    foreach ($resarr as $res)
      $ret[] = new $class($this, $res);
    return $ret;
  }

  public function getVocabularies() {
    $vocs = $this->graph->allOfType('onki:Vocabulary');
    return $this->createDataObjects("Vocabulary", $vocs);
  }

  public function getVocabulariesInCategory($cat) {
    $vocs = $this->graph->resourcesMatching('dc:subject', $cat);
    return $this->createDataObjects("Vocabulary", $vocs);
  }

  public function getVocabularyCategories() {
    $cats = $this->graph->allOfType('skos:Concept');
    return $this->createDataObjects("VocabularyCategory", $cats);
  }

  public function getVocabulary($vocid) {
    $vocs = $this->getVocabularies();
    foreach ($vocs as $voc) {
      if ($voc->getId() == $vocid) {
        return $voc;
      }
    }
    throw new Exception("Vocabulary id '$vocid' not found in configuration.");
  }

  /**
   * Return the vocabulary that is stored in the given graph on the given endpoint.
   *
   * @param $graph string graph URI
   * @param $endpoint string endpoint URL (default SPARQL endpoint if omitted)
   * @return Vocabulary vocabulary of this URI, or null if not found
   */
  public function getVocabularyByGraph($graph, $endpoint = DEFAULT_ENDPOINT) {
    $vocs = $this->getVocabularies();
    foreach ($vocs as $voc) {
      if ($voc->getGraph() == $graph && $voc->getEndpoint() == $endpoint)
        return $voc;
    }
    return null;
  }

  /**
   * Guess which vocabulary a URI originates from, based on the declared vocabulary URI spaces.
   *
   * @param $uri string URI to search
   * @return Vocabulary vocabulary of this URI, or null if not found
   */
  public function guessVocabularyFromURI($uri) {
    $vocs = $this->getVocabularies();
    foreach ($vocs as $voc) {
      if (strpos($uri, $voc->getUriSpace()) === 0)
        return $voc;
    }
    throw new Exception("Could not guess vocabulary.");
    return null;
  }

  public function getSparqlImplementation($dialect, $endpoint, $graph) {
    $classname = $dialect . "Sparql";
    return new $classname($endpoint, $graph);
  }

  public function getDefaultSparql() {
    return $this->getSparqlImplementation(DEFAULT_SPARQL_DIALECT, DEFAULT_ENDPOINT, '?graph');
  }

}

class VocabularyDataObject {

  public $dao;  // VocabularyDAO instance which created this object
  public $resource; // EasyRdf resource representing this object
  public $lang;  // language for human readable texts to be returned

  public function __construct($dao, $resource) {
    $this->dao = $dao;
    $this->resource = $resource;
    // get language from locale, same as used by gettext, set by Controller
    $this->lang = substr(getenv("LC_ALL"), 0, 2);
  }

}

class VocabularyCategory extends VocabularyDataObject {

  public function getVocabularies() {
    return $this->dao->getVocabulariesInCategory($this->resource);
  }

  public function getTitle() {
    return $this->resource->label($this->lang)->getValue();
  }

}

class Vocabulary extends VocabularyDataObject {

  public function getId() {
    $uriparts = explode("#", $this->resource->getURI());
    if (count($uriparts) != 1)
    // hash namespace
      return $uriparts[1];
    // slash namespace
    $uriparts = explode("/", $voc->getURI());
    return $uriparts[count($uriparts) - 1];
  }

  public function getTitle() {
    $literal = $this->resource->getLiteral('dc:title', $this->lang);
    if ($literal)
      return $literal->getValue();
    // not found with selected language, try any language
    return $this->resource->getLiteral('dc:title')->getValue();
  }

  /**
   * Get the languages supported by this vocabulary
   *
   * @return array languages supported by this vocabulary (as language tag strings)
   */
  public function getLanguages() {
    $langs = $this->resource->allLiterals('onki:language');
    $ret = array();
    foreach ($langs as $lang) {
      $ret[] = $lang->getValue();
    }
    return $ret;
  }

  /**
   * Get the SPARQL endpoint URL for this vocabulary
   *
   * @return string endpoint URL
   */
  public function getEndpoint() {
    return $this->resource->get('void:sparqlEndpoint');
  }

  /**
   * Get the SPARQL graph URI for this vocabulary
   *
   * @return string graph URI
   */
  public function getGraph() {
    $graph = $this->resource->get('onki:sparqlGraph');
    if ($graph)
      $graph = $graph->getUri();
    return $graph;
  }

  /**
   * Get the SPARQL implementation for this vocabulary
   *
   * @return Sparql SPARQL object
   */
  public function getSparql() {
    $endpoint = $this->getEndpoint();
    $graph = $this->getGraph();
    $dialect = $this->resource->get('onki:sparqlDialect');
    $dialect = $dialect ? $dialect->getValue() : DEFAULT_SPARQL_DIALECT;
    return $this->dao->getSparqlImplementation($dialect, $endpoint, $graph);
  }

  /**
   * Get the URI space of concepts in this vocabulary.
   *
   * @return string full URI of concept
   */
  public function getUriSpace() {
    return $this->resource->getLiteral('void:uriSpace')->getValue();
  }

  /**
   * Get the full URI of a concept in a vocabulary.
   *
   * @param $lname string local name of concept
   * @return string full URI of concept
   */
  public function getConceptURI($lname) {
    return $this->getUriSpace() . $lname;
  }

  /**
   * Get the localname of a concept in the vocabulary. If the URI is not
   * in the URI space of this vocabulary, return the full URI.
   *
   * @param $lname string full URI of concept
   * @return string local name of concept, or original full URI if the local name cannot be determined
   */
  public function getLocalName($uri) {
    return str_replace($this->getUriSpace(), "", $uri);
  }

  /**
   * Retrieves all the information about the Vocabulary
   * from the SPARQL-endpoint.
   */
  public function getInfo() {
    $ret = array();

    // get metadata from vocabulary configuration file
    foreach ($this->resource->properties() as $prop) {
      foreach ($this->resource->allLiterals($prop, $this->lang) as $val) {
        $ret[$prop] = $val->getValue();
      }
      foreach ($this->resource->allResources($prop) as $val) {
        $label = $val->label($this->lang);
        if ($label) {
          $ret[$prop] = $label->getValue();
        }
      }
    }

    // also include ConceptScheme metadata from SPARQL endpoint
    $sparql = $this->getSparql();

    // figure out which ConceptScheme to query for
    $cs = $this->resource->get('onki:mainConceptScheme');
    if (!$cs) {
      // mainConceptScheme not explicitly set, guess it using a SPARQL query
      $result = $sparql->listConceptSchemes();
      foreach ($result as $row) {
        $cs = $row->cs;
      }
    }

    // query everything the endpoint knows about the ConceptScheme
    $result = $sparql->queryConceptScheme($cs);
    $cs = $result->resource($cs);

    foreach ($cs->properties() as $prop) {
      foreach ($cs->allLiterals($prop, $this->lang) as $val) {
        $ret[$prop] = $val->getValue();
      }
      if (!isset($ret[$prop])) { // not found with language tag
        foreach ($cs->allLiterals($prop, null) as $val) {
          $ret[$prop] = $val->getValue();
        }
      }
    }
    if (isset($ret['owl:versionInfo'])) { // if version info availible for vocabulary convert it to a more readable format
      $ret['owl:versionInfo'] = $this->parseVersionInfo($ret['owl:versionInfo']);
    }
    return $ret;
  }
  
  /**
   * Tries to parse version, date and time from sparql version information into a readable format.
   * @param string $version
   * @return string
   */
  private function parseVersionInfo($version) {
    $chopped = explode(' ', $version);
    for ($i = 1; $i < sizeof($chopped) - 1; $i++) {
      if (strstr($chopped[$i], '-') && !strstr($chopped[$i], 'YSO')) {
        $ret = str_replace('-', '.', $chopped[$i]) . '&nbsp&nbsp&nbsp&nbsp' . substr($chopped[$i + 1], 0, -1);
        return $ret;
      } else if (strstr($chopped[$i], '/')) {
        $ret = $chopped[$i - 1] . '&nbsp&nbsp' . str_replace('/', '.', $chopped[$i]) . '&nbsp&nbsp' . $chopped[$i + 1];
        return $ret;
      }
    }
    return $version;
  }

  /**
   * Counts the statistics of the vocabulary.
   * @return type 
   */
  public function getStatistics() {
    $sparql = $this->getSparql();
    $ret = array();
    // find the number of concepts
    $ret['concepts'] = $sparql->countConcepts();
    // count the number of different types of concepts in all languages
    $ret['terms'] = $sparql->countLangConcepts();
    return $ret;
  }

  /** get the URL from which the vocabulary data can be downloaded
   */
  function getDataURL() {
    $graph = $this->getSparql()->graph;
    return SPARQL_GRAPH_STORE . "?graph=$graph";
  }

  function getHasGlobalCollections() {
    $val = $this->resource->getLiteral("onki:globalCollections");
    if ($val)
      return (boolean) $val->getValue();
    return false;
  }

}

?>
